unit Wiki;

interface
uses AsphyreDef, Classes;

type
TWikiImage = record
  x,y:real;
  Width,Height:real;
  ImageName:string;
end;

TWikiChain = class
private
  FParent:TWikiChain;
  FChains:array of TWikiChain;
  FName:string;
  FOpened:boolean;
  FContainment:string;
  FImages:array of TWikiImage;
  FParentWiki:TObject;
  function GetImage(Index:integer):TWikiImage;
  function GetChain(Index:integer):TWikiChain;
public
  procedure AddImage(const ImageName:string; x,y,width,height:real);
  function AddChain(const AName,AContainment:string):TWikiChain;
  constructor Create(const AName,AContainment:string);
  function ChainCount:integer;
  function ImageCount:integer;
  function Render:integer;
  property Images[Index: Integer]:TWikiImage read GetImage;
  property Chains[Index: Integer]:TWikiChain read GetChain;
  property Parent:TWikiChain read FParent write FParent;
  property Name:string read FName write FName;
  property Containment:string read FContainment write FContainment;
  property Wiki:TObject read FParentWiki write FParentWiki;
end;

TWiki = class
private
  FStartChains:array of TWikiChain;
  FListScrollY,FPageScrollY:real;
  FListLastY:real;
  FPageLastY:real;
  FPageX,FPageY,FPageWidth,FPageHeight:real;
  FListX,FListY,FListWidth,FListHeight:real;
  FSelectedChain:TWikiChain;
  FFontNumb:integer;
  FFontScale:real;
  function GetChain(Index:integer):TWikiChain;
  procedure AddFromString(s:string);
public
  function ChainCount:integer;
  function AddChain(const AName,AContainment:string):TWikiChain;
  function SearchByName(AName:string):TWikiChain;
  procedure Render;
  procedure Load(path:String);
  property Chains[Index: Integer]:TWikiChain read GetChain;
  property ListScrollY:real read FListScrollY write FListScrollY;
  property PageScrollY:real read FPageScrollY write FPageScrollY;
  property PageX:real read FPageX write FPageX;
  property PageY:real read FPageY write FPageY;
  property PageWidth:real read FPageWidth write FPageWidth;
  property PageHeight:real read FPageHeight write FPageHeight;
  property ListX:real read FListX write FListX;
  property ListY:real read FListY write FListY;
  property ListWidth:real read FListWidth write FListWidth;
  property ListHeight:real read FListHeight write FListHeight;
  property FontNumb:integer read FFontNumb write FFontNumb;
  property FontScale:real read FFontScale write FFontScale;
  property SelectedChain:TWikiChain read FSelectedChain write FSelectedChain;
end;

procedure OpenChains(Chain:TWikiChain);
procedure CloseChains(Chain:TWikiChain);
procedure DestroyChains(chain:TWikiChain);
procedure AddToChainFromStr(Chain:TWikiChain; s:string);
function Search(Name:string; Chain:TWikiChain):TWikiChain;
function GetFullPath(Chain:TWikiChain):string;
function RenderList(Chain:TWikiChain;Wiki:TWiki; Point:TPoint2; ScrollY:real; LastInList:boolean):TPoint2;


implementation

uses Main, Scripts, SysUtils, Forms, Math, AsphyreFonts,Controls, StrUtils,
 Geometry;

procedure OpenChains(Chain:TWikiChain);
begin
  Chain.FOpened:=true;

  if Chain.FParent<>nil then
  OpenChains(Chain.FParent);
end;

procedure CloseChains(Chain:TWikiChain);
var
i:integer;
begin
  Chain.FOpened:=false;

  for i := 0 to Chain.ChainCount-1 do
  CloseChains(Chain.Chains[i]);
end;

procedure DestroyChains(chain:TWikiChain);
var
i:integer;
begin
  for i := 0 to Chain.ChainCount-1 do
  DestroyChains(Chain.FChains[i]);
  Chain.Free;
end;

procedure AddToChainFromStr(Chain:TWikiChain; s:string);
var
i,c,c1,c2,c3:integer;
s1,s2,s3:string;
r:TMethInp;
t:TWikiChain;
begin
  while Pos('{',s)>0 do
  begin
    if PosEx('}',s,2)>0 then
    c2:=PosEx('}',s,2);
    if PosEx('{',s,2)>0 then
    c2:=Min(c2,PosEx('{',s,2));
    t:=Chain.AddChain('','');
    repeat
      c3:=pos('img<',s);
      if not((c3=0)or(c3>c2))then
      begin
        Delete(s,c3,4);
        r:=MakeInp(Copy(s,c3,PosEx('>',s,c3)-c3));
        Delete(s,c3,PosEx('>',s,c3)-c3);
        t.AddImage(r[0],StrToFloat(r[1]),StrToFloat(r[2]),
          StrToFloat(r[3]),StrToFloat(r[4]));
      end;
    until (c3=0)or(c3>c2);
    c:=FindEnd(s,'{','}',0);
    c1:=Pos('<',s);
    if PosEx('}',s,2)>0 then
    c2:=PosEx('}',s,2);
    if PosEx('{',s,2)>0 then
    c2:=Min(c2,PosEx('{',s,2));

    s1:=Copy(s,2,c1-2);
    s2:=Copy(s,c1,c2-c1);
    s3:=Copy(s,c2,c-c2+2);

    Delete(s,1,c2-1);
    t.FName:=s1;
    t.FContainment:=s2;
    AddToChainFromStr(t,s3);
    Delete(s,1,c-c2+2);
  end;
end;

function GetFullPath(Chain:TWikiChain):string;
begin
  if Chain.FParent<>nil then
  result:=GetFullPath(Chain.FParent,);
  result:=result+'\'+Chain.FName;
end;

function Search(Name:string; Chain:TWikiChain):TWikiChain;
var
i:integer;
s:string;
WikiCh:TWikiChain;
begin
  result:=nil;
  s:=GetFullPath(Chain);
  if Name=s then
  result:=Chain
  else
  for i := 0 to Chain.ChainCount-1 do
  begin
    WikiCh:=Search(Name,Chain.FChains[i]);
    if WikiCh<>nil then
    begin
      result:=WikiCh;
      exit;
    end;
  end;
end;

function RenderList(Chain:TWikiChain;Wiki:TWiki; Point:TPoint2; ScrollY:real; LastInList:boolean):TPoint2;
var
i:integer;
r,d:real;
z:TPoint2;
begin
  z:=Point;
  with MainForm do
  begin
    r:=Fonts[Wiki.FFontNumb].Scale;
    Fonts[Wiki.FFontNumb].Scale:=Wiki.FFontScale;

    z.y:=z.y+Fonts[Wiki.FFontNumb].TextHeight(Chain.FName);
    if OverlapRect2(Bounds2(Point.x,Wiki.FListLastY-ScrollY,
      Fonts[Wiki.FFontNumb].TextWidth(Chain.FName),
      Fonts[Wiki.FFontNumb].TextHeight(Chain.FName)),
      Bounds2(Wiki.FListX,Wiki.FListY,Wiki.FListWidth,Wiki.FListHeight)) then
    begin
      if PointInRect(Mouse.CursorPos,Bounds(round(Point.x),round(Wiki.FListLastY-ScrollY),
        round(Fonts[Wiki.FFontNumb].TextWidth(Chain.FName)),
        round(Fonts[Wiki.FFontNumb].TextHeight(Chain.FName)))) then
      begin
        if Mouses.Pressed[0] and (wait>20) then
        if Wiki.SelectedChain<>Chain then
        begin
          OpenChains(Chain);
          Wiki.SelectedChain:=Chain;
          Wiki.FPageScrollY:=0;
          Wait:=0;
        end
        else
        begin
          CloseChains(Chain);
          Wiki.SelectedChain:=nil;
          Wait:=0;
        end;
        Fonts[Wiki.FFontNumb].TextOut(Chain.FName,Point.x,Wiki.FListLastY-ScrollY,clRed4[1],clBlack4[1],fxBright)
      end
      else
      if Wiki.SelectedChain=Chain then
      Fonts[Wiki.FFontNumb].TextOut(Chain.FName,Point.x,Wiki.FListLastY-ScrollY,clYellow4[1])
      else
      Fonts[Wiki.FFontNumb].TextOut(Chain.FName,Point.x,Wiki.FListLastY-ScrollY,
        clWhite4[1]);
      Canvas.Line(Point2(z.x,z.y-Fonts[Wiki.FFontNumb].TextHeight(Chain.FName)/
        2-ScrollY),Point2(z.x-20,z.y-
        Fonts[Wiki.FFontNumb].TextHeight(Chain.FName)/2-ScrollY),
        clWhite4[0],clWhite4[0],fxBlend);
    end;
    z.x:=z.x+10;
//    if LastInList then
    result:=z;
    Wiki.FListLastY:=z.y;
    if Chain.FOpened then
    begin
      for i := 0 to Chain.ChainCount-1 do
      if i=CHain.ChainCount-1 then
      d:=RenderList(Chain.FChains[i],Wiki,Point2(z.x,Wiki.FListLastY),ScrollY,i=
        (Chain.ChainCount-1)).y
      else
      RenderList(Chain.FChains[i],Wiki,Point2(z.x,Wiki.FListLastY),ScrollY,i=
        (Chain.ChainCount-1));
      if Chain.ChainCount>0 then
      Canvas.Line(Point2(Point.x-10,Min(Max(Point.y-ScrollY+
        Fonts[Wiki.FFontNumb].TextHeight(Chain.FName)/2,Wiki.FListY),
        Wiki.FListY+Wiki.FListHeight+Fonts[Wiki.FFontNumb].TextHeight('w'))),
        Point2(Point.x-10,Max(Min(d-ScrollY-
        Fonts[Wiki.FFontNumb].TextHeight(Chain.FName)/2,Wiki.FListY+
        Wiki.FListHeight),Wiki.FListY-Fonts[Wiki.FFontNumb].TextHeight('w')/2)),clWhite4[0],clWhite4[0],fxBlend);
    end;
{    if not LastInList then
    result:=z;                             }
    Fonts[Wiki.FFontNumb].Scale:=r;
  end;
end;

function TWikiChain.GetImage(Index:integer):TWikiImage;
begin
  result:=FImages[Index];
end;

function TWikiChain.GetChain(Index:integer):TWikiChain;
begin
  result:=FChains[Index];
end;

procedure TWikiChain.AddImage(const ImageName:string; x,y,width,height:real);
var
c:integer;
begin
  c:=Length(FImages);
  SetLength(FImages,c+1);
  FImages[c].x:=x;
  FImages[c].y:=y;
  FImages[c].Width:=Width;
  FImages[c].Height:=Height;
  FImages[c].ImageName:=ImageName;
end;

constructor TWikiChain.Create(const AName,AContainment:string);
begin
  FName:=AName;
  FContainment:=AContainment;
end;

function TWikiChain.AddChain(const AName,AContainment:string):TWikiChain;
var
c:integer;
begin
  c:=Length(FChains);
  SetLength(FChains,c+1);
  FChains[c]:=TWikiChain.Create(AName,AContainment);
  FChains[c].FParent:=self;
  FChains[c].FParentWiki:=FParentWiki;
  result:=FChains[c];
end;

function TWikiChain.ChainCount:integer;
begin
  Result:=Length(FChains);
end;

function TWikiChain.ImageCount:integer;
begin
  Result:=Length(FImages);
end;

function TWikiChain.Render:integer;
var
i,r,texty:integer;
s,s1,ss1:string;
inp:TMethInp;
begin
  with MainForm do
  begin
    for i := 0 to ImageCount-1 do
    begin
      Canvas.DrawEx(Images.Image[FImages[i].ImageName],0,
        TWiki(FParentWiki).FPageX+FImages[i].x,TWiki(FParentWiki).FPageY+
        FImages[i].y-TWiki(FParentWiki).FPageScrollY, FImages[i].Width/
        Images.Image[FImages[i].ImageName].PatternSize.x,
        FImages[i].Height/Images.Image[FImages[i].ImageName].PatternSize.y,
        false,false,false,clWhite4,fxBlend);
      Canvas.FrameRect(round(TWiki(FParentWiki).FPageX+FImages[i].x),
        round(TWiki(FParentWiki).FPageY+FImages[i].y-
          TWiki(FParentWiki).FPageScrollY),round(FImages[i].Width),
          round(FImages[i].Height),$FFFFFFFF,fxBlend);
    end;
    s:=FContainment;
    texty:=round(TWiki(FParentWiki).FPageY-TWiki(FParentWiki).FPageScrollY);
    while Pos(#13#10,s)>0 do
    Delete(s,Pos(#13#10,s),2);
    repeat
      r:=FindEnd(s,'<','>',0);
      s1:=Copy(s,2,r-1);
      Delete(s,1,r+1);
      r:=FindEnd(s1,'(',')',0);
      ss1:=Copy(s1,2,r-1);
      inp:=MakeInp(ss1);
      Delete(s1,1,r+1);
      texty:=Scene.TextLines(s1,
        TWiki(FParentWiki).FPageX+StrToInt(inp[0]),
        texty,
        StrToFloat(inp[5]),
        StrToInt(inp[1]),
        StrToInt(inp[2]),
        StrToInt(inp[3]),
        Min(StrToInt(inp[4]),round(TWiki(FParentWiki).FPageWidth)),
        fxBlend,
        TWiki(FParentWiki).FPageY,
        TWiki(FParentWiki).FPageY+TWiki(FParentWiki).FPageHeight)
    until (s='')or(Pos('<',s)=0)or(texty>TWiki(FParentWiki).FPageY+
        TWiki(FParentWiki).FPageHeight);
  end;
  result:=texty;
end;

function TWiki.GetChain(Index:integer):TWikiChain;
begin
  Result:=FStartChains[Index];
end;

function TWiki.ChainCount:integer;
begin
  Result:=Length(FStartChains);
end;

function TWiki.AddChain(const AName,AContainment:string):TWikiChain;
var
c:integer;
begin
  c:=Length(FStartChains);
  SetLength(FStartChains,c+1);
  FStartChains[c]:=TWikiChain.Create(AName,AContainment);
  FStartChains[c].FParentWiki:=self;
  result:=FStartChains[c];
end;

function TWiki.SearchByName(AName:string):TWikiChain;
var
i:integer;
WikiCh:TWikiChain;
begin
  result:=nil;
  for i := 0 to ChainCount-1 do
  begin
    WikiCh:=Search(AName,FStartChains[i]);
    if WikiCh<>nil then
    begin
      result:=WikiCh;
      exit;
    end;
  end;
end;

procedure TWiki.Render;
var
i:integer;
begin
  with MainForm do
  begin
    Canvas.Rectangle(round(FListX),round(FListY-
      Fonts[FFontNumb].TextHeight('w')/2),round(FListWidth),
      round(FListHeight+Fonts[FFontNumb].TextHeight('w'))
    ,clWhite4,cColorAlpha4($0011FF,100),fxBlend);
  end;

  FListScrollY:=Max(0,FListScrollY);
  FListScrollY:=Min(FListLastY-FListHeight-FListY,FListScrollY);
  if FListLastY-FListY<FListHeight then
  FListScrollY:=0;
  FPageScrollY:=Max(0,FPageScrollY);

  FListLastY:=FListY;
  for i := 0 to ChainCount-1 do
  RenderList(FStartChains[i],self,Point2(FListX+15,FListLastY),
    ListScrollY,true);
  if Self.SelectedChain<>nil then
  FPageLastY:=self.SelectedChain.render;
end;

procedure TWiki.AddFromString(s:string);
var
i,c,c1,c2,c3:integer;
s1,s2,s3:string;
r:TMethInp;
t:TWikiChain;
begin
  while Pos(#13#10,s)>0 do
  Delete(s,Pos(#13#10,s),2);
  repeat
    if PosEx('}',s,2)>0 then
    c2:=PosEx('}',s,2);
    if PosEx('{',s,2)>0 then
    c2:=Min(c2,PosEx('{',s,2));
    t:=AddChain('','');
    repeat
      c3:=pos('img<',s);
      if not((c3=0)or(c3>c2))then
      begin
        Delete(s,c3,4);
        r:=MakeInp(Copy(s,c3,PosEx('>',s,c3)-c3));
        Delete(s,c3,PosEx('>',s,c3)-c3+1);
        T.AddImage(r[0],StrToFloat(r[1]),StrToFloat(r[2]),
          StrToFloat(r[3]),StrToFloat(r[4]));
      end;
    until (c3=0)or(c3>c2);
    c:=FindEnd(s,'{','}',0);
    c1:=Pos('<',s);
    if PosEx('}',s,2)>0 then
    c2:=PosEx('}',s,2);
    if PosEx('{',s,2)>0 then
    c2:=Min(c2,PosEx('{',s,2));
    s1:=Copy(s,2,c1-2);
    s2:=Copy(s,c1,c2-c1);
    s3:=Copy(s,c2,c-c2+2);
    Delete(s,1,c2-1);
    t.FName:=s1;
    t.FContainment:=s2;
    AddToChainFromStr(t,s3);
    Delete(s,1,c-c2+2);
  until Pos('{',s)=0;
end;

procedure TWiki.Load(path:String);
var
i:integer;
S:TStringList;
str:string;
begin
  for i:=0 to ChainCount-1 do
  DestroyChains(FStartChains[i]);
  
  S:=TStringList.Create;
  S.LoadFromFile(path);
  str:=s.Text;
  S.Free;
  AddFromString(str);
end;

end.
